/**
 * lee-gantt chart
 *
 * @source https://gitee.com/leeatao/lee-gantt
 * @license MIT
 * @author liyt
 * 2024-04-03
 */
(function ($, undefined) {
    "use strict";
    //每行高度
    $.fn.gantt = function (options) {

        var settings = {
            extendDays: 0,//前后延伸天数
            title: '',//任务标题
            weeks: ["日", "一", "二", "三", "四", "五", "六"],
            months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
            leftWidth: 200,//左侧宽度
            boxSize: 30,//任务窗格大小，宽高值
            data: [],
            staticStartDate: '',//固定开始时间
            staticEndDate: '',//固定结束时间
            lineBorderClass:'gantt-task-line-border',
            onItemClick: null,
            onItemTitleClick: null,
            onColumnClick: null,
            onLoadFinish: null
        };

        $.extend(settings, options);

        var core = {
            init: function (element) {
                element.leftWidth = settings.leftWidth;
                element.data = settings.data;
                core.initElement(element);
                core.create(element);
                core.loadData(element);
                if (settings.onLoadFinish != null && $.isFunction(settings.onLoadFinish)) {
                    settings.onLoadFinish();
                }
            },
            create: function (element) {
                //甘特面板
                var ganttPanel = $('<div class="gantt-panel"></div>');
                ganttPanel.append(core.createLeftTopPanel(element));
                ganttPanel.append(core.createLeftPanel(element));
                ganttPanel.append(core.createTopPanel(element));
                ganttPanel.append(core.createCenterPanel(element));
                $(element).append(ganttPanel);
            },
            //创建左上标题面板
            createLeftTopPanel: function (element) {
                var ganttLeftTop = $('<div class="gantt-lt">' + settings.title + '</div>');
                ganttLeftTop.width(settings.leftWidth);
                ganttLeftTop.height(settings.boxSize * 4);
                return ganttLeftTop;
            },
            //创建左侧面板
            createLeftPanel: function (element) {
                var leftPanel = $('<div class="gantt-left"></div>');
                leftPanel.css("padding-top", settings.boxSize * 4);
                leftPanel.width(settings.leftWidth + element.dayArray.length * settings.boxSize);
                leftPanel.append(core.createLeftHead(element));
                return leftPanel;
            },
            createLeftHead: function (element) {
                var leftHead = $('<div class="gantt-left-head"></div>');
                leftHead.height(element.data.length * settings.boxSize);
                leftHead.width(settings.leftWidth);
                $.each(element.data, function () {
                    var _this = this;
                    var leftRow = $('<div class="gantt-left-row" title="' + (this.desc ? this.desc:'') + '">' + this.title + '</div>');
                    leftRow.height(settings.boxSize);
                    if (settings.onItemTitleClick != null && $.isFunction(settings.onItemTitleClick)) {
                        leftRow.click(function () {
                            settings.onItemTitleClick(_this);
                        });
                    }
                    leftHead.append(leftRow);
                });
                return leftHead;
            },
            //创建顶部面板
            createTopPanel: function (element) {
                var topPanel = $('<div class="gantt-top"></div>');
                topPanel.css("padding-left", settings.leftWidth);
                topPanel.height(settings.boxSize * 4 + element.data.length * settings.boxSize);
                return topPanel.append(core.createTopHead(element));
            },
            //创建顶部头内容
            createTopHead: function (element) {

                var topHead = $('<div class="gantt-top-head"></div>');
                topHead.height(settings.boxSize * 4);
                topHead.width(element.dayArray.length * settings.boxSize);
                var yearHeadLine = $('<div class="gantt-top-head-line">');
                for (var [key, info] of element.yearMap) {
                    var yearColumn = $('<div class="gantt-top-col">' + info.label + '年</div>');
                    yearColumn.width(info.num * settings.boxSize);
                    yearColumn.height(settings.boxSize);
                    yearHeadLine.append(yearColumn);
                }
                topHead.append(yearHeadLine);

                var monthHeadLine = $('<div class="gantt-top-head-line">');
                for (var [key, info] of element.monthMap) {
                    var monthColumn = $('<div class="gantt-top-col">' + info.label + '</div>');
                    monthColumn.width(info.num * settings.boxSize);
                    monthColumn.height(settings.boxSize);
                    monthHeadLine.append(monthColumn);
                }
                topHead.append(monthHeadLine);

                var dayHeadLine = $('<div class="gantt-top-head-line">');
                $.each(element.dayArray, function () {
                    var dayColumn = $('<div class="gantt-top-col">' + this.label + '</div>');
                    dayColumn.width(settings.boxSize);
                    dayColumn.height(settings.boxSize);
                    dayHeadLine.append(dayColumn);
                });
                topHead.append(dayHeadLine);

                var weekHeadLine = $('<div class="gantt-top-head-line">');

                $.each(element.dayArray, function () {
                    var weekColumn = $('<div class="gantt-top-col">' + this.weekLabel + '</div>');
                    weekColumn.width(settings.boxSize);
                    weekColumn.height(settings.boxSize);
                    weekHeadLine.append(weekColumn);
                });
                topHead.append(weekHeadLine);


                return topHead;
            },
            //创建中心面板
            createCenterPanel: function (element) {
                var centerPanel = $('<div class="gantt-center"></div>');
                centerPanel.css("left", settings.leftWidth);
                centerPanel.css("top", settings.boxSize * 4);
                centerPanel.height(element.data.length * settings.boxSize);
                centerPanel.width(element.dayArray.length * settings.boxSize);
                $.each(element.data, function (i, task) {
                    $.each(element.dayArray, function (j, day) {
                        var dataColum = $('<div class="gantt-center-column" data-x="' + j + '" data-y="' + i + '" id="' + task.id + '_' + day.id + '"></div>');
                        dataColum.width(settings.boxSize);
                        dataColum.height(settings.boxSize);
                        if (settings.onColumnClick != null && $.isFunction(settings.onColumnClick)) {
                            dataColum.click(function () {
                                settings.onColumnClick(day);
                            });
                        }
                        centerPanel.append(dataColum);
                    });
                });
                element.centerPanel = centerPanel;
                return centerPanel;
            },
            loadData: function (element) {
                $.each(element.data, function (i, task) {
                    var taskId = task.id;
                    $.each(this.values, function (i, value) {
                        if (tool.isValidDate(value.startDate) && tool.isValidDate(value.endDate)) {

                            var tStartDate = new Date(value.startDate);
                            var tEndDate = new Date(value.endDate);

                            if (tEndDate >= element.startDate && tStartDate <= element.endDate) {
                                if (tStartDate < element.startDate) {
                                    tStartDate = element.startDate;
                                }
                                if (tEndDate > element.endDate) {
                                    tEndDate = element.endDate;
                                }
                            } else {
                                return true;
                            }

                            var columnId = taskId + '_' + tStartDate.getTime();

                            var columnElement = $(element).find('#' + columnId);
                            var isShowProgress = false;
                            if (value.progress && value.progress != '') {
                                isShowProgress = true;
                            }
                            var labelText = (value.label ? value.label : '') + (isShowProgress ? ' (' + value.progress + '%)' : '');
                            var taskLineBox = $('<div class="gantt-task" title="' + (value.toolTip ? value.toolTip : labelText) + '"></div>');
                            if (value.skin) {
                                taskLineBox.addClass(value.skin);
                            }
                            taskLineBox.css('left', columnElement.data('x') * settings.boxSize);
                            taskLineBox.css('top', columnElement.data('y') * settings.boxSize);
                            var days = tool.getDaysBetweenDates(tStartDate, tEndDate);
                            taskLineBox.height(settings.boxSize);
                            taskLineBox.width(days * settings.boxSize);
                            var taskLine = $('<div class="gantt-task-line"></div>');
                            if (isShowProgress) {
                                var taskBar = $('<div class="gantt-task-line-bar"></div>');
                                taskBar.css('width', value.progress + '%');
                                taskLine.append(taskBar);
                            }
                            taskLine.addClass(settings.lineBorderClass);
                            taskLineBox.append(taskLine);
                            var label = $('<div class="gantt-task-line-label">' + labelText + '</div>');
                            taskLineBox.append(label)
                            if (settings.onItemClick != null && $.isFunction(settings.onItemClick)) {
                                taskLineBox.click(function () {
                                    settings.onItemClick(value);
                                });
                            }
                            element.centerPanel.append(taskLineBox);
                        }
                    });
                });
            },
            initElement: function (element) {
                var taskList = element.data;
                var minDate = null;
                var maxDate = null;
                $.each(taskList, function () {
                    $.each(this.values, function () {
                        if (!tool.isValidDate(this.startDate)) {
                            return true;
                        }
                        if (!tool.isValidDate(this.endDate)) {
                            return true;
                        }
                        //任务开始时间大于结束时间的不合理情况，跳过处理
                        if (new Date(this.startDate).getTime() > new Date(this.endDate).getTime()) {
                            return true;
                        }

                        if (minDate == null) {
                            minDate = new Date(this.startDate);
                        }
                        if (maxDate == null) {
                            maxDate = new Date(this.endDate);
                        }

                        if (minDate.getTime() > new Date(this.startDate).getTime()) {
                            minDate = new Date(this.startDate);
                        }

                        if (maxDate.getTime() < new Date(this.endDate).getTime()) {
                            maxDate = new Date(this.endDate);
                        }
                    });
                });

                if (minDate == null) {
                    minDate = new Date();
                }
                if (maxDate == null) {
                    maxDate = new Date();
                }
                element.endDate = tool.addDays(maxDate, settings.extendDays);
                element.startDate = tool.reduceDays(minDate, settings.extendDays);
                if (settings.staticStartDate != '') {
                    element.startDate = new Date(settings.staticStartDate);
                }
                if (settings.staticEndDate != '') {
                    element.endDate = new Date(settings.staticEndDate);
                }

                var yearMap = new Map();
                var monthMap = new Map();
                var dayArray = [];

                for (var current = new Date(element.startDate); current <= new Date(element.endDate); current.setDate(current.getDate() + 1)) {

                    var dayInfo = {};
                    dayInfo.date = current;
                    dayInfo.id = current.getTime();
                    dayInfo.label = current.getDate();
                    dayInfo.weekLabel = settings.weeks[current.getDay()];
                    dayArray.push(dayInfo);

                    var yearStr = tool.dateToStr(current, 'year');
                    if (yearMap.has(yearStr)) {
                        var yearInfo = yearMap.get(yearStr);
                        yearInfo.num = yearInfo.num + 1;
                        yearMap.set(yearStr, yearInfo);
                    } else {
                        var yearInfo = {};
                        yearInfo.date = current;
                        yearInfo.label = current.getFullYear();
                        yearInfo.num = 1;
                        yearMap.set(yearStr, yearInfo);
                    }
                    var monthStr = tool.dateToStr(current, 'month');
                    if (monthMap.has(monthStr)) {
                        var monthInfo = monthMap.get(monthStr);
                        monthInfo.num = monthInfo.num + 1;
                        monthMap.set(monthStr, monthInfo);
                    } else {
                        var monthInfo = {};
                        monthInfo.date = current;
                        monthInfo.label = settings.months[(current.getMonth())];
                        monthInfo.num = 1;
                        monthMap.set(monthStr, monthInfo);
                    }
                }
                element.dayArray = dayArray;
                element.yearMap = yearMap;
                element.monthMap = monthMap;

            }

        }
        var tool = {
            logDate: function (date) {
                console.info(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate());
            },
            getDaysBetweenDates: function (sDate, eDate) {
                eDate.setDate(eDate.getDate() + 1);
                // 计算两个日期的时间差（毫秒）
                var timeDiff = Math.abs(eDate.getTime() - sDate.getTime());
                // 计算天数
                var days = Math.ceil(timeDiff / (1000 * 3600 * 24));
                return days;
            },
            //日期转为字符串-type:day,month,year
            dateToStr: function (date, type) {
                var yyyy = date.getFullYear();
                var mm = String(date.getMonth() + 1).padStart(2, '0'); // 月份需要加1，并且补零
                var dd = String(date.getDate()).padStart(2, '0'); // 日需要补零
                if (type == 'year') {
                    return `${yyyy}`;
                } else if (type == 'month') {
                    return `${yyyy}-${mm}`;
                } else {
                    return `${yyyy}-${mm}-${dd}`;
                }
            },
            //日期减去指定天数
            reduceDays: function (date, days) {
                return new Date(date.setDate(date.getDate() - days));
            },
            //日期增加指定天数
            addDays: function (date, days) {
                return new Date(date.setDate(date.getDate() + days));
            },
            //校验日期格式
            isValidDate: function (strDate) {
                var regex = /^\d{4}-\d{2}-\d{2}$/;
                var date = new Date(strDate);
                return date instanceof Date && date.toString() !== 'Invalid Date' && regex.test(strDate);
            },
            //比较日期大小
            compareDates: function (sDate, eDate) {
                var time1 = new Date(sDate).getTime();
                var time2 = new Date(eDate).getTime();
                if (time1 > time2) {
                    return 1;
                } else if (time1 < time2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        }

        this.each(function () {
            this.startDate = null;
            this.endDate = null;
            this.dayArray = [];
            this.yearMap = new Map();
            this.monthMap = new Map();
            this.lineNum = 0;
            this.columNum = 0;
            this.leftWidth = null;
            this.data = null;
            this.centerPanel = null;
            $(this).empty();
            core.init(this);
        });

    };
})(jQuery);